package core

import (
	"crypto/elliptic"
	"crypto/sm2"
	"errors"
	"fmt"
	"github.com/ethereum/go-ethereum/common/math"
	"math/big"
)

const (
	SM3       = 0x01
	MD4       = 0x02
	MD5       = 0x03
	SHA1      = 0x04
	SHA224    = 0x05
	SHA256    = 0x06
	SHA384    = 0x07
	SHA512    = 0x08
	RIPEMD160 = 0x09
)

const (
	SM2 = 0x01
	RSA = 0x02
)

var (
	sm2p256N, _ = new(big.Int).SetString("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16)
)

// toECDSA creates a private key with the given D value. The strict parameter
// controls whether the key's length should be enforced at the curve size or
// it can also accept legacy encodings (0 prefixes).
func UnmarshalECDSA(d []byte, strict bool) (*sm2.PrivateKey, error) {
	priv := &sm2.PrivateKey{}
	priv.PublicKey.Curve = sm2.P256Sm2()
	if strict && 8*len(d) != priv.Params().BitSize {
		return nil, fmt.Errorf("invalid length, need %d bits", priv.Params().BitSize)
	}
	priv.D = new(big.Int).SetBytes(d)

	// The priv.D must < N
	if priv.D.Cmp(sm2p256N) >= 0 {
		return nil, fmt.Errorf("invalid private key, >=N")
	}
	// The priv.D must not be zero or negative.
	if priv.D.Sign() <= 0 {
		return nil, fmt.Errorf("invalid private key, zero or negative")
	}

	priv.PublicKey.X, priv.PublicKey.Y = priv.PublicKey.Curve.ScalarBaseMult(d)
	if priv.PublicKey.X == nil {
		return nil, errors.New("invalid private key")
	}
	return priv, nil
}

// FromECDSA exports a private key into a binary dump.
func FromECDSA(priv *sm2.PrivateKey) []byte {
	if priv == nil {
		return nil
	}
	return math.PaddedBigBytes(priv.D, priv.Params().BitSize/8)
}

// UnmarshalPubkey converts bytes to a secp256k1 public key.
func UnmarshalPubkey(pub []byte) (*sm2.PublicKey, error) {
	x, y := elliptic.Unmarshal(sm2.P256Sm2(), pub)
	if x == nil {
		return nil, errors.New("UnmarshalPubkey Fail")
	}
	return &sm2.PublicKey{Curve: sm2.P256Sm2(), X: x, Y: y}, nil
}

func FromECDSAPub(pub *sm2.PublicKey) []byte {
	if pub == nil || pub.X == nil || pub.Y == nil {
		return nil
	}
	return elliptic.Marshal(sm2.P256Sm2(), pub.X, pub.Y)
}
